Model.aSyncInsert   F
last analyzed

Complexity

Conditions 19

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
eloc 5
dl 0
loc 5
rs 0.5999
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like Model.aSyncInsert often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import HasManyThrough from './Model/Relation/HasManyThrough';
2
import HasMany from "./Model/Relation/HasMany";
3
import BelongsTo from "./Model/Relation/BelongTo";
4
import MorphOne from "./Model/Relation/MorphOne";
5
import HasOne from "./Model/Relation/HasOne";
6
import HasOneThrough from "./Model/Relation/HasOneThrough";
7
import MorphTo from "./Model/Field/MorphTo";
8
import Field from "./Model/Field";
9
import Relation from "./Model/Relation";
10
import ForeignKey from "./Model/Field/ForeignKey";
11
import Index from "./Table/Index";
12
import {ModelInterface, ModelStaticInterface} from "../JeloquentInterfaces";
13
import Collection from "./Collection";
14
import * as Str from "../Util/Str";
15
import * as Obj from "../Util/Obj";
16
import * as ModelSetup from "./Model/Util/Setup";
17
import Table from "./Table";
18
19
class Model implements ModelInterface {
20
21
    private static kebabCaseName: string;
22
23
    private static snakeCaseName: string;
24
25
    _tmpId: string;
26
27
    ['constructor']: ModelStaticInterface;
28
29
    private _originalFields: Map<string, Field>;
30
31
    private _primaryFields: Field[];
32
33
    private numberOfFields: number;
34
35
    constructor(fields: Field[] = []) {
36
        this.setFields(ModelSetup.addRelationFieldsToList(fields));
37
        this._tmpId = `_${++globalThis.Store.numberOfModelCreated}`;
38
39
        return ModelSetup.modelProxy(this);
40
    }
41
42
    static get className(): string {
43
        return this.name;
44
    }
45
46
    static get kebabCaseClassName(): string {
47
        return this.kebabCaseName ??= Str.KebabCase(this.className);
48
    }
49
50
    static get snakeCaseClassName(): string {
51
        return this.snakeCaseName ??= Str.SnakeCase(this.className);
52
    }
53
54
    get className(): string {
55
        return Object.getPrototypeOf(this).constructor.className;
56
    }
57
58
    get dirtyFieldNames(): string[] {
59
        return this.dirtyFields.map(field => field.name);
60
    }
61
62
    get dirtyFields(): Field[] {
63
        return this.originalFields.filter(field => field.isDirty);
64
    }
65
66
    get kebabCaseClassName(): string {
67
        return  Object.getPrototypeOf(this).constructor.kebabCaseClassName;
68
    }
69
70
    get originalFields(): Field[] {
71
        return [...this._originalFields.values()];
72
    }
73
74
    get originalPrimaryKey(): unknown {
75
        return this.primaryFields.reduce((toValue, field, i) => {
76
            if (i > 0) {
77
                return `${toValue}-${field.originalValue}`;
78
            }
79
            return field.originalValue;
80
        }, '') ?? this._tmpId ?? null;
81
    }
82
83
    get originalValues(): object {
84
        return this.originalFields.reduce((originalValues, field) => {
85
            if (field.originalValue !== undefined) {
86
                originalValues[field.name] = field.originalValue;
87
            }
88
            return originalValues;
89
        }, {});
90
    }
91
92
    get primaryFields(): Field[] {
93
        return this._primaryFields ??= this.originalFields.filter(field => field.isPrimary);
94
    }
95
96
    get primaryKey(): string {
97
        return (this.primaryFields.reduce((toValue:string, field:Field, i:number): string => {
98
            if (i > 0) {
99
                return `${toValue}-${field.value}`;
100
            }
101
102
            if (field.value === null) {
103
                return field.value;
104
            }
105
106
            return `${field.value}`;
107
        }, '') ?? this._tmpId ?? null);
108
    }
109
110
111
    get primaryKeyName(): string[] {
112
        return this.originalFields.filter(field => field.isPrimary).map(field => field.name);
113
    }
114
115
116
117
    get snakeCaseClassName(): string {
118
        return Object.getPrototypeOf(this).constructor.snakeCaseClassName;
119
    }
120
121
    static aSyncInsert(data: object): Promise<Collection> {
122
        return new Promise((resolve) => {
123
            queueMicrotask(() => {
124
                resolve(this.insert(data));
125
            });
126
        });
127
    }
128
129
    static aSyncUpdate(data: object): Promise<Collection> {
130
        return new Promise((resolve) => {
131
            queueMicrotask(() => {
132
                resolve(this.update(data));
133
            });
134
        });
135
    }
136
137
    static all(): Collection {
138
        return globalThis.Store.database().all(this.className);
139
    }
140
141
    static delete(id): void {
142
        globalThis.Store.database().delete(this.className, id);
143
    }
144
145
    static find(id): unknown {
146
        return globalThis.Store.database().find(this.className, id);
147
    }
148
149
    static getIndexByKey(indexName) {
150
        return globalThis
151
            .Store
152
            .database()
153
            .getIndexByKey(this.className, indexName);
154
    }
155
156
    static getInstance(): ModelInterface {
157
        const original = globalThis.Store.classInstances[this.className] ?? (globalThis.Store.classInstances[this.className] = new this())
158
        const fieldsClone = original.originalFields.reduce((obj, field) => {
159
            obj.push(Object.assign(Object.create(Object.getPrototypeOf(field)), field));
160
            return obj;
161
        }, [])
162
163
        return ModelSetup.modelProxy(Object.create(Object.getPrototypeOf(original)).setFields(fieldsClone));
164
    }
165
166
    static ids() {
167
        return globalThis
168
            .Store
169
            .database()
170
            .ids(this.className);
171
    }
172
173
    static insert(data: object|object[]): Collection {
174
        const modelsData = Array.isArray(data) ? data : [data];
175
        const length = modelsData.length;
176
        const models = new Collection();
177
        for (let i = 0; i < length; i++) {
178
            const modelData = modelsData[i];
179
            const model = this.getInstance();
180
            model.fill(modelData);
181
            globalThis.Store.database().insert(this.className, model);
182
            model.fillRelations(modelData);
183
            models.push(model);
184
        }
185
        return models;
186
    }
187
188
    static registerIndex(name: string): void {
189
        Index.register(this.getInstance(), name);
190
    }
191
192
    static update(data: object|object[]): Collection {
193
        const modelsData = Array.isArray(data) ? data : [data];
194
        const length = modelsData.length;
195
        const models = new Collection();
196
        for (let i = 0; i < length; i++) {
197
            const model = this.find(this.getInstance().primaryKeyByValues(data));
198
            model.fill(data);
199
            globalThis.Store.database().update(this.className, model);
200
            model.fillRelations(data);
201
            models.push(model);
202
        }
203
        return models;
204
    }
205
206
    delete() {
207
        Object.getPrototypeOf(this).constructor.delete(this.primaryKey);
208
    }
209
210
    fill(data) {
211
        for (let i = 0; i < this.numberOfFields; i++) {
212
            if (!(this.originalFields[i] instanceof Relation)) {
213
                const fieldName = this.originalFields[i].name;
214
                if (data[fieldName] !== undefined) {
215
                    this[`_${fieldName}`] = data[fieldName];
216
                }
217
            }
218
        }
219
    }
220
221
    fillRelations(data: object): void {
222
        // insert through relations after model insert;
223
        for (let i = 0; i < this.numberOfFields; i++) {
224
            if ((this.originalFields[i] instanceof Relation)) {
225
                const fieldName = this.originalFields[i].name;
226
                if (data[fieldName] !== undefined) {
227
                    this[`_${fieldName}`] = data[fieldName];
228
                }
229
            }
230
        }
231
    }
232
233
    isDirty(fieldName:string = null) {
234
        if (fieldName) {
235
            return this.dirtyFieldNames.includes(fieldName);
236
        }
237
        return this.dirtyFields.length > 0;
238
    }
239
240
    primaryKeyByValues(values): string
241
    {
242
        return this.primaryFields.reduce((toValue, field:Field, i) => {
243
            if (i > 0) {
244
                return `${toValue}-${values[field.name]}`
245
            }
246
            return `${values[field.name]}`;
247
        }, '');
248
    }
249
250
    registerIndex(name) {
251
        Index.register(this, name);
252
    }
253
254
    resetDirty() {
255
        this.originalFields.filter((field) => !(field instanceof Relation)).forEach(field => {
256
            field.resetDirty();
257
        })
258
    }
259
260
    save() {
261
        globalThis.Store.database().save(this.className, this);
262
    }
263
264
    setFields(fields: Field[]) {
265
        this._originalFields = new Map();
266
        ModelSetup.setFields(this, fields);
267
        return this;
268
    }
269
270
    tableSetup(table: Table) {
271
        ModelSetup.setupTable(this, table);
272
    }
273
274
    toJSON(): object {
275
        return this.toObject();
276
    }
277
278
    toObject(fromRelation = false): object {
279
        return Obj.fromModel(this, fromRelation);
280
    }
281
}
282
283
export {
284
    Model,
285
    Field,
286
    Relation,
287
    BelongsTo,
288
    HasOne,
289
    HasOneThrough,
290
    HasMany,
291
    HasManyThrough,
292
    MorphOne,
293
    MorphTo,
294
    ForeignKey,
295
};